昨天介紹了利用 segue 達到正向傳值的方法,今天要介紹另外兩個逆向傳值的方法,有些其中的原理並沒有理解的很清楚,在講述上如果有錯誤,麻煩大家指正。
此方法是設定好協定 (protocol) 和委任 (delegate),達到傳值,適用一對一 view 的傳值,先設定好兩個畫面,用此方法傳值,必須搭配不使用 segue 的轉場方式,即直接用語法來完成轉場,此篇在第二個 view 上利用滾輪選擇,來回到第一個 view,使第一個 view 的背景顏色轉變為選擇的顏色,也剛好利用此專案來練習 enumeration 搭配 switch 的使用方式
Storyboard 設定的畫面如下,第一個 view 放置到下一頁的按鈕,第二個 view 放置一個 pick view 和一個回到前一個 view 的按鈕,並且為第二個 view 的 Storyboard Identifier 命名,詳情可參閱第三種轉場方法
在第二頁面設定好 pick view 的設定,這次使用 enumeration 來做管理
enum ColorSet: Int, CaseIterable {
case Grey = 0, Red, Green, Orange, Blue
var description: String {
switch self {
case .Grey: return "Grey"
case .Red: return "Red"
case .Green: return "Green"
case .Orange: return "Orange"
case .Blue: return "Blue"
}
}
var color: UIColor {
switch self {
case .Grey: return UIColor.gray
case .Red: return UIColor.red
case .Green: return UIColor.green
case .Orange: return UIColor.orange
case .Blue: return UIColor.blue
}
}
}
// MARK: - PickView Setting
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return ColorSet.allCases.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return ColorSet.init(rawValue: row)?.description
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
backgroundColor = ColorSet.init(rawValue: row)!.color
}
接著進行傳值的設定,在第二個 view controller 設定需要實作的 Protocol
protocol BackgroundColorDelegate {
func colorSelected(_ color: UIColor)
}
並且設定 delegate 的變數
var delegate: BackgroundColorDelegate?
在按下返回的按鈕後把從 pick view 選定的值放入 protocol 的方法中
@IBAction func back(_ sender: UIButton) {
delegate?.colorSelected(backgroundColor)
dismiss(animated: true, completion: nil)
}
在第一個 view controller 利用 extension 採用 protocol 並實作其方法
extension ViewController: BackgroundColorDelegate {
func colorSelected(_ color: UIColor) {
view.backgroundColor = color
}
}
記得在第一個 view 中的下一頁按鈕,設定委任
let secondVC = storyboard?.instantiateViewController(withIdentifier: "secondVC") as! SecondViewController
secondVC.delegate = self
// Can't combine with performSegue
present(secondVC, animated: true, completion: nil)
實際執行結果
為方便參考,在此放上完整專案: github
此方法利用 Notification 的監聽模式來傳值,適用一對多 view 的傳值,同樣也是先設定好兩個畫面
先在第一個 view 中設定 notification key,其形式為 global 變數,而 key 必須是獨一無二的
let redNotificationKey = "com.jes-yang.red"
let greenNotificationKey = "com.jes-yang.green"
設定要監測的名稱
let red = Notification.Name(rawValue: redNotificationKey)
let green = Notification.Name(rawValue: greenNotificationKey)
加入此程式碼取消監測(經查詢,說法不一,有的說在 iOS9 之後以不需要再加入此行,不過在學習的時候,影片仍是有強調需要加入)
deinit {
NotificationCenter.default.removeObserver(self)
}
官方手冊中關於是否該加入方法 removeObserver()
利用 addObserver(_: selector: name: object:)
方法來讓使用觀察者和通知選擇器以及可選的通知名稱和發送者,將一個條目添加到通知中心的調度表中
// Red
NotificationCenter.default.addObserver(self, selector: #selector(updateTextLabel(notification:)), name: red, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(updateBackground(notification:)), name: red, object: nil)
// Green
NotificationCenter.default.addObserver(self, selector: #selector(updateTextLabel(notification:)), name: green, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(updateBackground(notification:)), name: green, object: nil)
觀測方法中的 selector 函數
@objc func updateTextLabel(notification: NSNotification) {
let isRed = notification.name == red
let text = isRed ? "Red" : "Green"
textLabel.text = text
}
@objc func updateBackground(notification: NSNotification) {
let isRed = notification.name == red
let color = isRed ? UIColor.red : UIColor.green
view.backgroundColor = color
}
在第二個 view,利用方法 post(name: object:)
創建具有給定名稱和發件人的通知,並將其發佈到通知中心,將此方法放入 view 中的 button's action 中
@IBAction func redButtonTapped(_ sender: UIButton) {
let name = Notification.Name(rawValue: redNotificationKey)
NotificationCenter.default.post(name: name, object: nil)
dismiss(animated: true, completion: nil)
}
@IBAction func greenButtonTapped(_ sender: UIButton) {
let name = Notification.Name(rawValue: greenNotificationKey)
NotificationCenter.default.post(name: name, object: nil)
dismiss(animated: true, completion: nil)
}
實際執行結果
為方便參考,在此放上完整專案: github
終於來到鐵人賽的最後一天,沒想到自己可以堅持下來並完成挑戰,這三十天來,紀錄自己在過去幾個月中學習到複習並整理,也偶爾分享一些新試玩的東西,藉由這些紀錄,在查找的過程,也釐清了一些之前不懂的觀念,學習之路並不會因為鐵人賽的結束而終止,希望未來自己能夠更上一層,吸收更多新知後再分享給大家。